using Cairo;
using Gtk;

namespace Ribbons {

	/**
	 * Ribbon theme.
	 *
	 * Used to draw ribbon widgets.
	 */
	public class Theme : GLib.Object {

		public enum ButtonState {
			DEFAULT,
			HOVER,
			PRESSED,
		}

		public enum MenuItemState {
			DEFAULT,
			HILIGHT_ACTION,
			HILIGHT_MENU,
			HILIGHT,
		}

		private ColorScheme _color_scheme = new ColorScheme ();

		public void draw_application_menu (Context cr, Gdk.Rectangle r, Gdk.Rectangle items_alloc,
											double line_width, ApplicationMenu w) {
			double line_width_05 = line_width / 2;
			double line_width_15 = line_width * 1.5;
			Gdk.Rectangle alloc = (Gdk.Rectangle) w.allocation;

			cr.set_source_rgb (0.4, 0.4, 0.4);
			cr.paint ();

			cr.rectangle (items_alloc.x, items_alloc.y, items_alloc.width, items_alloc.height);
			cr.set_source_rgb (0.9216, 0.9216, 0.9216);
			cr.fill ();

			cr.set_line_width (line_width);

			cr.move_to (Gdk.Rect.right (items_alloc) - line_width_05, Gdk.Rect.top (items_alloc));
			cr.line_to (Gdk.Rect.right (items_alloc) - line_width_05, Gdk.Rect.bottom (items_alloc));
			cr.set_source_rgba (1, 1, 1, 0.2);
			cr.stroke ();

			cr.move_to (Gdk.Rect.right (items_alloc) - line_width_15, Gdk.Rect.top (items_alloc));
			cr.line_to (Gdk.Rect.right (items_alloc) - line_width_15, Gdk.Rect.bottom (items_alloc));
			cr.set_source_rgba (0, 0, 0, 0.2);
			cr.stroke ();

			cr.rectangle (alloc.x, alloc.y, alloc.width, items_alloc.y - alloc.y);
			var lin_grad = new Pattern.linear (0, alloc.y, 0, items_alloc.y - alloc.y);
			lin_grad.add_color_stop_rgb (0.0, 0.4, 0.4, 0.4);
			lin_grad.add_color_stop_rgb (0.3, 0.2, 0.2, 0.2);
			lin_grad.add_color_stop_rgb (0.3, 0, 0, 0);
			lin_grad.add_color_stop_rgb (1.0, 0.4, 0.4, 0.4);
			cr.set_source (lin_grad);
			cr.fill ();

			cr.rectangle (alloc.x, Gdk.Rect.bottom (items_alloc), alloc.width,
							Gdk.Rect.bottom (alloc) - Gdk.Rect.bottom (items_alloc));
			lin_grad = new Pattern.linear (0, Gdk.Rect.bottom (items_alloc),
											0, Gdk.Rect.bottom (alloc));
			lin_grad.add_color_stop_rgb (0.0, 0.4, 0.4, 0.4);
			lin_grad.add_color_stop_rgb (0.3, 0.2, 0.2, 0.2);
			lin_grad.add_color_stop_rgb (0.3, 0, 0, 0);
			lin_grad.add_color_stop_rgb (1.0, 0.4, 0.4, 0.4);
			cr.set_source (lin_grad);
			cr.fill ();

			Gdk.Rectangle app_btn_alloc = (Gdk.Rectangle) w.application_button.allocation;
			app_btn_alloc.x = alloc.x;
			app_btn_alloc.y = items_alloc.y - 2 - app_btn_alloc.height;
			draw_application_button (cr, app_btn_alloc, ButtonState.PRESSED, 1.0,
										w.application_button);
		}

		public void draw_application_menu_item (Context cr, Gdk.Rectangle body_allocation,
											MenuItemState state, double round_size,
											double line_width, double arrow_size,
											double arrow_padding, bool draw_separator,
											ApplicationMenuItem widget) {
			double line_width_05 = line_width / 2;
			double line_width_15 = line_width_05 * 3;
			double separator_x = body_allocation.x + body_allocation.width
								- 2 * line_width - arrow_size - 2 * arrow_padding;

			cr.set_line_width (line_width);

			if (state != MenuItemState.DEFAULT) {
				Pattern body_pattern;
				Pattern inner_border_pattern;
				Color border_color;

				body_pattern = new Pattern.linear (
								body_allocation.x, body_allocation.y,
								body_allocation.x, body_allocation.y + body_allocation.height);
				body_pattern.add_color_stop_rgb (0.0, 1, 0.996, 0.890);
				body_pattern.add_color_stop_rgb (0.37, 1, 0.906, 0.592);
				body_pattern.add_color_stop_rgb (0.43, 1, 0.843, 0.314);
				body_pattern.add_color_stop_rgb (1.0, 1, 0.906, 0.588);

				inner_border_pattern = new Pattern.linear (
								body_allocation.x, body_allocation.y,
								body_allocation.x + body_allocation.width,
								body_allocation.y + body_allocation.height);
				inner_border_pattern.add_color_stop_rgba (0.0, 1, 1, 0.969, 1);
				inner_border_pattern.add_color_stop_rgba (1.0, 1, 1, 0.969, 0);

				border_color = Color.from_rgb (0.824, 0.753, 0.553);

				double x0 = body_allocation.x + line_width_05;
				double y0 = body_allocation.y + line_width_05;
				double x1 = body_allocation.x + body_allocation.width - line_width_05;
				double y1 = body_allocation.y + body_allocation.height - line_width_05;

				cr.move_to (x0 + round_size, y0);
				cr.arc (x1 - round_size, y0 + round_size, round_size, 1.5 * Math.PI, 0);
				cr.arc (x1 - round_size, y1 - round_size, round_size, 0, 0.5 * Math.PI);
				cr.arc (x0 + round_size, y1 - round_size, round_size, 0.5 * Math.PI, Math.PI);
				cr.arc (x0 + round_size, y0 + round_size, round_size, Math.PI, 1.5 * Math.PI);

				cr.set_source (body_pattern);
				cr.fill ();

				if (state == MenuItemState.HILIGHT_ACTION) {
					cr.set_source_rgba (1, 1, 1, 0.7);
					cr.move_to (x0 + round_size, y0);
					cr.line_to (separator_x, y0);
					cr.line_to (separator_x, y1);
					cr.arc (x0 + round_size, y1 - round_size, round_size, 0.5 * Math.PI, Math.PI);
					cr.arc (x0 + round_size, y0 + round_size, round_size, Math.PI, 1.5 * Math.PI);
					cr.fill ();
				} else if (state == MenuItemState.HILIGHT_MENU) {
					cr.set_source_rgba (1, 1, 1, 0.7);
					cr.move_to (separator_x, y0);
					cr.arc (x1 - round_size, y0 + round_size, round_size, 1.5 * Math.PI, 0);
					cr.arc (x1 - round_size, y1 - round_size, round_size, 0, 0.5 * Math.PI);
					cr.line_to (separator_x, y1);
					cr.line_to (separator_x, y0);
					cr.fill ();
				}

				x0 = body_allocation.x + line_width_15;
				y0 = body_allocation.y + line_width_15;
				x1 = body_allocation.x + body_allocation.width - line_width_15;
				y1 = body_allocation.y + body_allocation.height - line_width_15;

				double round_size_minus_line_width = round_size - line_width;

				x0 -= line_width;

				cr.move_to (x0 + round_size_minus_line_width, y0);
				cr.arc (x1 - round_size_minus_line_width, y0 + round_size_minus_line_width,
						round_size_minus_line_width, 1.5 * Math.PI, 0);
				cr.arc (x1 - round_size_minus_line_width, y1 - round_size_minus_line_width,
						round_size_minus_line_width, 0, 0.5 * Math.PI);
				cr.arc (x0 + round_size_minus_line_width, y1 - round_size_minus_line_width,
						round_size_minus_line_width, 0.5 * Math.PI, Math.PI);
				cr.arc (x0 + round_size_minus_line_width, y0 + round_size_minus_line_width,
						round_size_minus_line_width, Math.PI, 1.5 * Math.PI);

				x0 += line_width;

				cr.set_source (inner_border_pattern);
				cr.stroke ();

				x0 = body_allocation.x + line_width_05;
				y0 = body_allocation.y + line_width_05;
				x1 = body_allocation.x + body_allocation.width - line_width_05;
				y1 = body_allocation.y + body_allocation.height - line_width_05;

				cr.move_to (x0 + round_size, y0);
				cr.arc (x1 - round_size, y0 + round_size, round_size, 1.5 * Math.PI, 0);
				cr.arc (x1 - round_size, y1 - round_size, round_size, 0, 0.5 * Math.PI);
				cr.arc (x0 + round_size, y1 - round_size, round_size, 0.5 * Math.PI, Math.PI);

				Cairo.set_color (cr, border_color);
				cr.stroke ();
			}

			if (arrow_size > 0) {
				double x, y;
				
				x = separator_x;
				y = body_allocation.y + (body_allocation.height - arrow_size) / 2.0;
				
				if (draw_separator) {
					double top = body_allocation.y + 2 * line_width;
					double bottom = body_allocation.y + body_allocation.height - 2 * line_width;
					cr.move_to (x - line_width / 2, top);
					cr.line_to (x - line_width / 2, bottom);
					cr.set_source_rgba (0, 0, 0, 0.1);
					cr.stroke ();
					
					cr.move_to (x + line_width / 2, top);
					cr.line_to (x + line_width / 2, bottom);
					cr.set_source_rgba (1, 1, 1, 0.6);
					cr.stroke ();
				}
				
				x += arrow_size / 4.0 + line_width / 2.0;
				
				cr.move_to (x, y);
				cr.line_to (x, y + arrow_size);
				cr.line_to (x + arrow_size / 2.0, y + arrow_size / 2.0);
				cr.line_to (x, y);
				cr.set_source_rgb (0, 0, 0);
				cr.fill ();
			}
		}

		/** Draws a group. */
		public void draw_group (Context cr, Gdk.Rectangle r, double round_size,
								double line_width, double space, Pango.Layout l,
								Gtk.Widget expand_button, RibbonGroup w) {
			double line_width_05 = line_width / 2, line_width_15 = 3 * line_width_05;
			Pattern lin_grad;

			double x0 = r.x + round_size;
			double x1 = r.x + r.width - round_size;
			double y0 = r.y + round_size;
			double y1 = r.y + r.height - round_size;
			cr.arc (x1, y1, round_size - line_width_05, 0, Math.PI / 2);
			cr.arc (x0 + line_width, y1, round_size - line_width_05, Math.PI / 2, Math.PI);
			cr.arc (x0, y0, round_size - line_width_15, Math.PI, 3 * Math.PI / 2);
			cr.arc (x1, y0 + line_width, round_size - line_width_05, 3 * Math.PI / 2, 0);
			cr.line_to (x1 + round_size - line_width_05, y1);
			cr.set_line_width (line_width);
			Cairo.set_color (cr, _color_scheme.bright);
			cr.stroke ();

			if (l != null) {
				int lbl_width, lbl_height;
				Pango.cairo_update_layout (cr, l);
				l.get_pixel_size (out lbl_width, out lbl_height);

				if (w.label_position == PositionType.TOP
						|| w.label_position == PositionType.BOTTOM) {
					double label_y;
					double band_height = lbl_height + 2 * space;

					if (w.label_position == PositionType.TOP) {
						cr.arc (x0, y0, round_size - line_width_05, Math.PI, 3 * Math.PI / 2);
						cr.arc (x1, y0, round_size - line_width_05, 3 * Math.PI / 2, 0);
						double band_y = y0 - round_size + 2 * line_width + band_height;
						cr.line_to (x1 + round_size - line_width_15, band_y);
						cr.line_to (x0 - round_size + line_width_05, band_y);
						lin_grad = new Pattern.linear (0, band_y - band_height, 0, band_y);
						Cairo.add_stop (lin_grad, 0.0, _color_scheme.dark);
						Cairo.add_stop (lin_grad, 1.0, _color_scheme.pretty_dark);
						cr.set_source (lin_grad);
						cr.fill ();

						label_y = band_y - band_height - space;
					} else {
						cr.arc (x1, y1, round_size - line_width_15, 0, Math.PI / 2);
						cr.arc (x0, y1 - line_width, round_size - line_width_05,
								Math.PI / 2, Math.PI);
						double band_y = y1 + round_size - 2 * line_width - band_height;
						cr.line_to (x0 - round_size + line_width_05, band_y);
						cr.line_to (x1 + round_size - line_width_15, band_y);
						lin_grad = new Pattern.linear (0, band_y, 0, band_y + band_height);
						Cairo.add_stop (lin_grad, 0.0, _color_scheme.dark);
						Cairo.add_stop (lin_grad, 1.0, _color_scheme.pretty_dark);
						cr.set_source (lin_grad);
						cr.fill ();

						label_y = band_y;
					}

					double frame_size = 2 * line_width + space;
					double available_horizontal_space = r.width - 2 * frame_size;
					if (expand_button.visible) {
						available_horizontal_space -= expand_button.width_request + space;
					}

					cr.save ();
					cr.rectangle (r.x + frame_size, label_y,
									available_horizontal_space, band_height);
					cr.clip ();
						
					cr.set_source_rgb (1, 1, 1);
					Pango.cairo_update_layout (cr, l);
					cr.move_to (r.x + frame_size
						+ Math.fmax (0, (available_horizontal_space - lbl_width) / 2),
						label_y + space);
					Pango.cairo_show_layout (cr, l);

					cr.restore ();
				} else {	// label at right or left
					double label_x;
					double band_width = lbl_height + 2 * space;

					if (w.label_position == PositionType.LEFT) {
						cr.arc (x0, y1, round_size - line_width_05, Math.PI / 2, Math.PI);
						cr.arc (x0, y0, round_size - line_width_05, Math.PI, 3 * Math.PI / 2);
						double band_x = x0 - round_size + 2 * line_width + band_width;
						cr.line_to (band_x, y0 - round_size + line_width_05);
						cr.line_to (band_x, y1 + round_size - line_width_15);
						lin_grad = new Pattern.linear (band_x - band_width, 0, band_x, 0);
						Cairo.add_stop (lin_grad, 0.0, _color_scheme.dark);
						Cairo.add_stop (lin_grad, 1.0, _color_scheme.pretty_dark);
						cr.set_source (lin_grad);
						cr.fill ();

						label_x = band_x - band_width - space;
					} else {
						cr.arc (x1, y0 - line_width_05, round_size - line_width_15,
								3 * Math.PI / 2, 0);
						cr.arc (x1, y1, round_size - line_width_15, 0, Math.PI / 2);
						double band_x = x1 + round_size - 2 * line_width - band_width;
						cr.line_to (band_x, y1 + round_size - line_width_15);
						cr.line_to (band_x, y0 - round_size + line_width_05);
						lin_grad = new Pattern.linear (band_x, 0, band_x + band_width, 0);
						Cairo.add_stop (lin_grad, 0.0, _color_scheme.dark);
						Cairo.add_stop (lin_grad, 1.0, _color_scheme.pretty_dark);
						cr.set_source (lin_grad);
						cr.fill ();

						label_x = band_x + space;
					}

					double frame_size = 2 * line_width + space;
					double available_vertical_space = r.height - 2 * frame_size;
					if (expand_button.visible) {
						available_vertical_space -= expand_button.height_request + space;
					}

					cr.save ();
					cr.rectangle (label_x, r.y + frame_size,
									band_width, available_vertical_space);
					cr.clip ();
					cr.rotate (-Math.PI / 2);

					cr.set_source_rgb (1, 1, 1);
					Pango.cairo_update_layout (cr, l);
					double shift = Math.fmax (0, (available_vertical_space - lbl_width) / 2);
					if (expand_button.visible) {
						shift += expand_button.height_request + space;
					}
					cr.move_to (-(r.y + r.height - 2 * space - shift), label_x + space);
					Pango.cairo_show_layout (cr, l);

					cr.restore ();
				}
			}

			cr.move_to (x1 + round_size - line_width_15, y1);
			cr.arc (x1, y1, round_size - line_width_15, 0, Math.PI / 2);
			cr.arc (x0, y1 - line_width, round_size - line_width_05, Math.PI / 2, Math.PI);
			cr.arc (x0, y0, round_size - line_width_05, Math.PI, 3 * Math.PI / 2);
			cr.arc (x1 - line_width, y0, round_size - line_width_05, 3 * Math.PI / 2, 0);
			cr.line_to (x1 + round_size - line_width_15, y1);
			cr.set_line_width (line_width);
			lin_grad = new Pattern.linear (0, r.y, 0, r.y + r.height - line_width);
			Cairo.add_stop (lin_grad, 0.0, ColorScheme.get_color_relative (
												_color_scheme.pretty_dark, 0.1));
			Cairo.add_stop (lin_grad, 1.0, _color_scheme.pretty_dark);
			cr.set_source (lin_grad);
			cr.stroke ();
		}

		public Gdk.Color get_forecolor_for_ribbon_tabs (bool selected) {
			if (selected) {
				return Gdk.new_color (0, 0, 0);
			} else {
				return Gdk.new_color (255, 255, 255);
			}
		}

		/** Draws a ribbon. */
		public void draw_ribbon (Context cr, Gdk.Rectangle menu_bar_allocation,
							Gdk.Rectangle body_allocation, double round_size,
							double line_width, Ribbon widget) {
			double line_width_05 = line_width / 2;
			double line_width_15 = 3 * line_width_05;
			double x0, x1, y0, y1;
			Pattern lin_grad;

			// XXX-GEETK: background painting
			cr.set_source_rgb (0.3, 0.3, 0.3);
			cr.paint ();
			// XXX-GEETK

			if (menu_bar_allocation.height > 0) {
				cr.rectangle (menu_bar_allocation.x, menu_bar_allocation.y,
								menu_bar_allocation.width, menu_bar_allocation.height - 1);
				lin_grad = new Pattern.linear (0, menu_bar_allocation.y, 0,
								menu_bar_allocation.y + menu_bar_allocation.height - 1);
				lin_grad.add_color_stop_rgba (0.0, 1, 1, 1, 0.5);
				lin_grad.add_color_stop_rgba (0.3, 1, 1, 1, 0.2);
				lin_grad.add_color_stop_rgba (0.3, 1, 1, 1, 0.0);
				lin_grad.add_color_stop_rgba (1.0, 1, 1, 1, 0.5);

				cr.set_source (lin_grad);
				cr.fill ();

				cr.move_to (menu_bar_allocation.x, Gdk.Rect.bottom (menu_bar_allocation) + 0.5);
				cr.line_to (Gdk.Rect.right (menu_bar_allocation), Gdk.Rect.bottom (menu_bar_allocation) + 0.5);
				cr.set_source_rgba (1, 1, 1, 0.5);
				cr.set_line_width (1);
				cr.stroke();

				// Quick Access Toolbar background

				Gdk.Rectangle alloc = (Gdk.Rectangle) widget.quick_access_toolbar.allocation;		// XXX copy?
				x0 = alloc.x;
				x1 = Gdk.Rect.right (alloc) - 1;
				y0 = alloc.y;
				y1 = Gdk.Rect.bottom (alloc) - 1;
				double radius = (y1 - y0) / 2;

				cr.set_line_width (1);

				if (widget.application_button != null) {
					Gdk.Rectangle alloc2 = (Gdk.Rectangle) widget.application_button.allocation;		// XXX
					double cx = alloc2.x + alloc2.width / 2;
					double cy = alloc2.y + alloc2.height / 2;
					double radius2 = x0 - cx;
					double alpha = Math.asin ((y0 - cy) / radius2);
					double beta = Math.asin ((y1 - cy) / radius2);
					double curve_width0 = Math.cos (Math.fabs (alpha)) * radius2;
					double curve_width1 = Math.cos (Math.fabs (beta)) * radius2;
					double curve_width = Math.fmin (curve_width0, curve_width1);

					cr.save ();
					cr.rectangle (cx + curve_width, y0, x1 - cx - curve_width, alloc.height);
					cr.clip_preserve ();
					cr.arc_negative (cx, cy, radius2, -alpha, -beta);
					lin_grad = new Pattern.linear (0, y0, 0, y1);
					Cairo.add_stop (lin_grad, 0.0, _color_scheme.bright);
					Cairo.add_stop (lin_grad, 1.0, _color_scheme.pretty_dark);
					cr.set_source (lin_grad);
//					cr.set_source_rgb (1, 0, 0);
					cr.fill ();
					cr.restore ();
					cr.arc (x1, y0 + radius, radius - 0.5, 1.5 * Math.PI, 0.5 * Math.PI);
					cr.set_source (lin_grad);
					cr.fill ();

					cr.arc (cx, cy, radius2, alpha, beta);
					cr.set_source_rgba (0, 0, 0, 0.6);
					cr.stroke ();
					radius2 -= 1;
					cr.arc (cx, cy, radius2, alpha, beta);
					cr.set_source_rgba (1, 1, 1, 0.4);
					cr.stroke ();

					cr.move_to (cx + curve_width0, y0 - 0.5);
					cr.line_to (x1, y0 - 0.5);
					cr.set_source_rgba (1, 1, 1, 0.4);
					cr.stroke ();

					cr.move_to (cx + curve_width0, y0 + 0.5);
					cr.line_to (x1, y0 + 0.5);
					cr.set_source_rgba (0, 0, 0, 0.6);
					cr.stroke ();

					cr.move_to (cx + curve_width1, y1 - 0.5);
					cr.line_to (x1, y1 - 0.5);
					cr.set_source_rgba (0, 0, 0, 0.6);
					cr.stroke ();

					cr.move_to (cx + curve_width1, y1 + 0.5);
					cr.line_to (x1, y1 + 0.5);
					cr.set_source_rgba (1, 1, 1, 0.4);
					cr.stroke ();
				} else {
					cr.rectangle (x0, y0, x1 - x0, alloc.height);
					lin_grad = new Pattern.linear (0, y0, 0, y1);
					Cairo.add_stop (lin_grad, 0.0, _color_scheme.bright);
					Cairo.add_stop (lin_grad, 1.0, _color_scheme.pretty_dark);
					cr.set_source (lin_grad);
					cr.fill ();

					cr.arc (x1, y0 + radius, radius - 0.5, 1.5 * Math.PI, 0.5 * Math.PI);
					cr.set_source (lin_grad);
					cr.fill ();

					cr.move_to (x0 + 0.5, y0);
					cr.line_to (x0 + 0.5, y1);
					cr.set_source_rgba (1, 1, 1, 0.4);
					cr.stroke ();

					cr.move_to (x0 + 1.5, y0);
					cr.line_to (x0 + 1.5, y1);
					cr.set_source_rgba (0, 0, 0, 0.6);
					cr.stroke ();
				}

				cr.arc (x1, y0 + radius, radius + 0.5, 1.5 * Math.PI, 0.5 * Math.PI);
				cr.set_source_rgba (1, 1, 1, 0.4);
				cr.stroke ();

				cr.arc (x1, y0 + radius, radius - 0.5, 1.5 * Math.PI, 0.5 * Math.PI);
				cr.set_source_rgba (0, 0, 0, 0.6);
				cr.stroke ();
			}

			RibbonPage p = widget.current_page;
			if (p == null) {
				return;
			}

//			Color c = _color_scheme.get_color_absoulte (_color_scheme.bright, 0.92);
			Color c = _color_scheme.normal;

			if (body_allocation.height > 0) {
				// *** PAGE ***

				x0 = body_allocation.x;
				x1 = body_allocation.x + body_allocation.width;
				y0 = body_allocation.y;
				y1 = body_allocation.y + body_allocation.height;

				cr.arc (x0 + round_size, y1 - round_size,
						round_size - line_width_05, Math.PI / 2, Math.PI);
				cr.arc (x0 + round_size, y0 + round_size,
						round_size - line_width_05, Math.PI, 3 * Math.PI / 2);
				cr.arc (x1 - round_size, y0 + round_size,
						round_size - line_width_05, 3 * Math.PI / 2, 0);
				cr.arc (x1 - round_size, y1 - round_size,
						round_size - line_width_05, 0, Math.PI/2);
				cr.line_to (x0 + round_size, y1 - line_width_05);

				// *** BACKGOUND ***
				Cairo.set_color (cr, c);
				cr.fill_preserve ();

				// *** DARK BORDER ***
				cr.set_line_width (line_width);
				Cairo.set_color (cr, ColorScheme.get_color_absolute (
												_color_scheme.normal, 0.75));
				cr.stroke ();

				// *** GLASS EFFECT ***
				double ymid = Math.round (y0 + (y1 - y0) * 0.25);
						
				cr.arc (x0 + round_size, y0 + round_size,
						round_size - line_width, Math.PI, 3 * Math.PI / 2);
				cr.arc (x1 - round_size, y0 + round_size,
						round_size - line_width, 3 * Math.PI / 2, 0);
				cr.line_to (x1 - line_width, ymid);
				cr.line_to (x0 + line_width, ymid);
				cr.line_to (x0 + line_width, y0 + round_size);
				lin_grad = new Pattern.linear (0, y0, 0, ymid);
				lin_grad.add_color_stop_rgba (0.0, 0, 0, 0, 0.0);
				lin_grad.add_color_stop_rgba (1.0, 0, 0, 0, 0.075);
				cr.set_source (lin_grad);
				cr.fill ();

				cr.arc (x0 + round_size, y1 - round_size,
						round_size - line_width, Math.PI / 2, Math.PI);
				cr.line_to (x0 + line_width, ymid);
				cr.line_to (x1 - line_width, ymid);
				cr.arc (x1 - round_size, y1 - round_size,
						round_size - line_width, 0, Math.PI / 2);
				cr.line_to (x0 + round_size, y1 - line_width);
				lin_grad = new Pattern.linear (0, ymid, 0, y1);
				lin_grad.add_color_stop_rgba (0.0, 0, 0, 0, 0.1);
				lin_grad.add_color_stop_rgba (0.5, 0, 0, 0, 0.0);
				cr.set_source (lin_grad);
				cr.fill ();
			}

			// *** TAB ***

			Gdk.Rectangle r = p.label_allocation;

			x0 = r.x;
			x1 = r.x + r.width;
			y0 = r.y;
			y1 = r.y + r.height + line_width;

			// *** TAB :: BACKGROUND ***

			cr.move_to (x0 + line_width_05, y1);
			cr.line_to (x0 + line_width_05, y0 + round_size);
			cr.arc (x0 + round_size, y0 + round_size,
					round_size - line_width_05, Math.PI, 3 * Math.PI / 2);
			cr.arc (x1 - round_size, y0 + round_size,
					round_size - line_width_05, 3 * Math.PI / 2, 0);
			cr.line_to (x1 - line_width_05, y1);

			lin_grad = new Pattern.linear (0, y0, 0, y1);
			Cairo.add_stop (lin_grad, 0.0, _color_scheme.pretty_bright);
			Cairo.add_stop (lin_grad, 1.0, c);
			cr.set_source (lin_grad);
			cr.fill ();

			// *** TAB :: DARK BORDER ***

			cr.move_to (x0 + line_width_05, y1);
			cr.line_to (x0 + line_width_05, y0 + round_size);
			cr.arc (x0 + round_size, y0 + round_size,
					round_size - line_width_05, Math.PI, 3 * Math.PI / 2);
			cr.arc (x1 - round_size, y0 + round_size,
					round_size - line_width_05, 3 * Math.PI / 2, 0);
			cr.line_to (x1 - line_width_05, y1);

			cr.set_line_width (line_width);
			Cairo.set_color (cr, ColorScheme.get_color_relative (_color_scheme.bright, -0.1));
			cr.stroke ();

			y1 -= 1.0;

			// *** TAB :: HIGHLIGHT ***

			cr.move_to (x0 + line_width_15, y1);
			cr.line_to (x0 + line_width_15, y0 + round_size);
			cr.arc (x0 + round_size, y0 + round_size,
					round_size - line_width_15, Math.PI, 3 * Math.PI / 2);
			cr.arc (x1 - round_size, y0 + round_size,
					round_size - line_width_15, 3 * Math.PI / 2, 0);
			cr.line_to (x1 - line_width_15, y1);

			cr.set_line_width (line_width);
			lin_grad = new Pattern.linear (0, y0 + line_width, 0, y1);
			Cairo.add_stop (lin_grad, 0.0, _color_scheme.pretty_bright);
			Cairo.add_stop (lin_grad, 1.0, ColorScheme.set_alpha_channel (
												_color_scheme.bright, 0));
			cr.set_source (lin_grad);
			cr.stroke ();

			// *** TAB :: SHADOW ***

			cr.move_to (x0 - line_width_05, y1);
			cr.line_to (x0 - line_width_05, y0 + round_size);
			cr.arc (x0 + round_size, y0 + round_size,
					round_size + line_width_05, Math.PI, 3 * Math.PI / 2);
			cr.arc (x1 - round_size, y0 + round_size,
					round_size + line_width_05, 3 * Math.PI / 2, 0);
			cr.line_to (x1 + line_width_05, y1);

			cr.set_line_width (line_width);
			cr.set_source_rgba (0, 0, 0, 0.2);
			cr.stroke ();
		}

		/** Draws an application button. */
		public void draw_application_button (Context cr, Gdk.Rectangle body_allocation,
											ButtonState state, double line_width,
											BaseButton widget) {
			double drop_shadow_offset = 1;		// XXX-GEETK: local consts not yet in Vala
			double radius = (body_allocation.height - drop_shadow_offset) / 2;

			double x = body_allocation.x + radius + drop_shadow_offset;
			double y = body_allocation.y + radius + drop_shadow_offset;
			cr.arc (x, y, radius, 0, 2 * Math.PI);
			cr.set_source_rgba (0, 0, 0, 0.5);
			cr.fill ();

			cr.arc (body_allocation.x + radius, body_allocation.y + radius, radius, 0, 2 * Math.PI);
			switch (state) {
			case ButtonState.HOVER:
				cr.set_source_rgb (0.9, 0.815, 0.533);
				break;
			case ButtonState.PRESSED:
				cr.set_source_rgb (0.886, 0.639, 0.356);
				break;
			default:
				cr.set_source_rgb (0.8, 0.8, 0.8);
				break;
			}
			cr.fill ();

			cr.arc (body_allocation.x + radius, body_allocation.y + radius, radius, 0, 2 * Math.PI);
			var lin_grad = new Pattern.linear (0, body_allocation.y, 0, body_allocation.y + body_allocation.height);
			lin_grad.add_color_stop_rgba (0.0, 1, 1, 1, 0.9);
			lin_grad.add_color_stop_rgba (0.5, 1, 1, 1, 0.0);
			lin_grad.add_color_stop_rgba (1.0, 1, 1, 1, 1.0);
			cr.set_source (lin_grad);
			cr.fill ();

			cr.arc (body_allocation.x + radius, body_allocation.y + radius, radius, 0, 2 * Math.PI);
			lin_grad = new Pattern.linear (0, body_allocation.y, 0, body_allocation.y + body_allocation.height);
			lin_grad.add_color_stop_rgba (0.0, 0, 0, 0, 0.0);
			lin_grad.add_color_stop_rgba (0.4, 0, 0, 0, 0.0);
			lin_grad.add_color_stop_rgba (0.5, 0, 0, 0, 0.1);
			lin_grad.add_color_stop_rgba (0.75, 0, 0, 0, 0.0);
			lin_grad.add_color_stop_rgba (1.0, 0, 0, 0, 0.0);
			cr.set_source (lin_grad);
			cr.fill ();

			cr.arc (body_allocation.x + radius, body_allocation.y + radius, radius, 0, Math.PI);
			lin_grad = new Pattern.linear (0, body_allocation.y + radius, 0, body_allocation.y + body_allocation.height);
			lin_grad.add_color_stop_rgba (0.0, 0, 0, 0, 0.0);
			lin_grad.add_color_stop_rgba (1.0, 0, 0, 0, 0.5);
			cr.set_source (lin_grad);
			cr.set_line_width (1.0);
			cr.stroke ();

			cr.arc (body_allocation.x + radius, body_allocation.y + radius, radius, Math.PI, 0);
			lin_grad = new Pattern.linear (0, body_allocation.y, 0, body_allocation.y + radius);
			lin_grad.add_color_stop_rgba (0.0, 1, 1, 1, 0.5);
			lin_grad.add_color_stop_rgba (1.0, 1, 1, 1, 0.0);
			cr.set_line_width (1.0);
			cr.stroke ();

			cr.arc (body_allocation.x + radius, body_allocation.y + radius, radius, 0, 2 * Math.PI);
			var rad_grad = new Pattern.radial (
					body_allocation.x + radius, body_allocation.y + 1.5 * radius, 0,
					body_allocation.x + radius, body_allocation.y + 1.5 * radius, 0.5 * radius);
			rad_grad.add_color_stop_rgba (0, 1, 1, 1, 0.4);
			rad_grad.add_color_stop_rgba (1, 1, 1, 1, 0.0);
			cr.set_source (rad_grad);
			cr.fill ();
		}

		/** Draws a button. */
		public void draw_button (Context cr, Gdk.Rectangle body_allocation,
								ButtonState state, double round_size,
								double line_width, double arrow_size,
								double arrow_padding, bool draw_separator,
								BaseButton widget) {
			double line_width_05 = line_width / 2;
			double line_width_15 = line_width_05 * 3;

			bool up_left = true;
			bool up_right = true;
			bool down_right = true;
			bool down_left = true;
			switch (widget.group_style) {
			case GroupStyle.LEFT:
				up_right = down_right = false;
				break;
			case GroupStyle.CENTER:
				up_left = down_left = up_right = down_right = false;
				break;
			case GroupStyle.RIGHT:
				up_left = down_left = false;
				break;
			}

			cr.set_line_width (line_width);

			if (state == ButtonState.PRESSED || state == ButtonState.HOVER) {
				Pattern body_pattern;
				Pattern inner_border_pattern;
				Color border_color;

				if (state == ButtonState.PRESSED) {
					body_pattern = new Pattern.linear (
							body_allocation.x,
							body_allocation.y,
							body_allocation.x,
							body_allocation.y + body_allocation.height);
					body_pattern.add_color_stop_rgb (0.0, 0.996, 0.847, 0.667);
					body_pattern.add_color_stop_rgb (0.37, 0.984, 0.710, 0.396);
					body_pattern.add_color_stop_rgb (0.43, 0.980, 0.616, 0.204);
					body_pattern.add_color_stop_rgb (1.0, 0.992, 0.933, 0.667);

					inner_border_pattern = new Pattern.linear (
							body_allocation.x,
							body_allocation.y,
							body_allocation.x + body_allocation.width,
							body_allocation.y + body_allocation.height);
					inner_border_pattern.add_color_stop_rgba (0.0, 0.876, 0.718, 0.533, 1);
					inner_border_pattern.add_color_stop_rgba (1.0, 0.876, 0.718, 0.533, 0);

					border_color = Color.from_rgb (0.671, 0.631, 0.549);
				} else {
					body_pattern = new Pattern.linear (
							body_allocation.x,
							body_allocation.y,
							body_allocation.x,
							body_allocation.y + body_allocation.height);
					body_pattern.add_color_stop_rgb (0.0, 1, 0.996, 0.890);
					body_pattern.add_color_stop_rgb (0.37, 1, 0.906, 0.592);
					body_pattern.add_color_stop_rgb (0.43, 1, 0.843, 0.314);
					body_pattern.add_color_stop_rgb (1.0, 1, 0.906, 0.588);

					inner_border_pattern = new Pattern.linear (
							body_allocation.x,
							body_allocation.y,
							body_allocation.x + body_allocation.width,
							body_allocation.x + body_allocation.height);
					inner_border_pattern.add_color_stop_rgba (0.0, 1, 1, 0.969, 1);
					inner_border_pattern.add_color_stop_rgba (1.0, 1, 1, 0.969, 0);

					border_color = Color.from_rgb (0.824, 0.753, 0.553);
				}

				double x0 = body_allocation.x + line_width_05;
				double y0 = body_allocation.y + line_width_05;
				double x1 = body_allocation.x + body_allocation.width - line_width_05;
				double y1 = body_allocation.y + body_allocation.height - line_width_05;

				if (up_left) {
					cr.move_to (x0 + round_size, y0);
				} else {
					cr.move_to (x0, y0);
				}
				if (up_right) {
					cr.arc (x1 - round_size, y0 + round_size, round_size, 1.5 * Math.PI, 0);
				} else {
					cr.line_to (x1, y0);
				}
				if (down_right) {
					cr.arc (x1 - round_size, y1 - round_size, round_size, 0, 0.5 * Math.PI);
				} else {
					cr.line_to (x1, y1);
				}
				if (down_left) {
					cr.arc (x0 + round_size, y1 - round_size, round_size, 0.5 * Math.PI, Math.PI);
				} else {
					cr.line_to (x0, y1);
				}
				if (up_left) {
					cr.arc (x0 + round_size, y0 + round_size, round_size, Math.PI, 1.5 * Math.PI);
				} else {
					cr.line_to (x0, y0);
				}

				cr.set_source (body_pattern);
				cr.fill ();

				x0 = body_allocation.x + line_width_15;
				y0 = body_allocation.y + line_width_15;
				x1 = body_allocation.x + body_allocation.width - line_width_15;
				y1 = body_allocation.y + body_allocation.height - line_width_15;

				double round_size_minus_line_width = round_size - line_width;

				if (widget.group_style != GroupStyle.LEFT) {
					x0 -= line_width;
				}

				if (up_left) cr.move_to (x0 + round_size_minus_line_width, y0);
				else cr.move_to (x0, y0);
				if (up_right) cr.arc (x1 - round_size_minus_line_width, y0 + round_size_minus_line_width, round_size_minus_line_width, 1.5 * Math.PI, 0);
				else cr.line_to (x1, y0);
				if (down_right) cr.arc (x1 - round_size_minus_line_width, y1 - round_size_minus_line_width, round_size_minus_line_width, 0, 0.5 * Math.PI);
				else cr.line_to (x1, y1);
				if (down_left) cr.arc (x0 + round_size_minus_line_width, y1 - round_size_minus_line_width, round_size_minus_line_width, 0.5 * Math.PI, Math.PI);
				else cr.line_to (x0, y1);
				if (up_left) cr.arc (x0 + round_size_minus_line_width, y0 + round_size_minus_line_width, round_size_minus_line_width, Math.PI, 1.5 * Math.PI);
				else cr.line_to (x0, y0);

				if (widget.group_style != GroupStyle.LEFT) {
					x0 += line_width;
				}

				cr.set_source (inner_border_pattern);
				cr.stroke ();

				x0 = body_allocation.x + line_width_05;
				y0 = body_allocation.y + line_width_05;
				x1 = body_allocation.x + body_allocation.width - line_width_05;
				y1 = body_allocation.y + body_allocation.height - line_width_05;

				if (up_left) cr.move_to (x0 + round_size, y0);
				else cr.move_to (x0, y0);
				if (up_right) cr.arc (x1 - round_size, y0 + round_size, round_size, 1.5 * Math.PI, 0);
				else cr.line_to (x1, y0);
				if (down_right) cr.arc (x1 - round_size, y1 - round_size, round_size, 0, 0.5 * Math.PI);
				else cr.line_to (x1, y1);
				if (down_left) cr.arc (x0 + round_size, y1 - round_size, round_size, 0.5 * Math.PI, Math.PI);
				else cr.line_to (x0, y1);
				if (widget.group_style == GroupStyle.LEFT) {
					if (up_left) cr.arc (x0 + round_size, y0 + round_size, round_size, Math.PI, 1.5 * Math.PI);
					else cr.line_to (x0, y0);
				}

				Cairo.set_color (cr, border_color);
				cr.stroke ();
			} else if (widget.draw_background) {
				var body_pattern = new Pattern.linear (
						body_allocation.x,
						body_allocation.y,
						body_allocation.x,
						body_allocation.y + body_allocation.height);
				body_pattern.add_color_stop_rgba (0.0, 1, 1, 1, 0.7);
				body_pattern.add_color_stop_rgba (0.37, 1, 1, 1, 0.2);
				body_pattern.add_color_stop_rgba (0.43, 1, 1, 1, 0.2);
				body_pattern.add_color_stop_rgba (1.0, 1, 1, 1, 0.7);

				double x0 = body_allocation.x + line_width_05;
				double y0 = body_allocation.y + line_width_05;
				double x1 = body_allocation.x + body_allocation.width - line_width_05;
				double y1 = body_allocation.y + body_allocation.height - line_width_05;

				if (up_left) cr.move_to (x0 + round_size, y0);
				else cr.move_to (x0, y0);
				if (up_right) cr.arc (x1 - round_size, y0 + round_size, round_size, 1.5 * Math.PI, 0);
				else cr.line_to (x1, y0);
				if (down_right) cr.arc (x1 - round_size, y1 - round_size, round_size, 0, 0.5 * Math.PI);
				else cr.line_to (x1, y1);
				if (down_left) cr.arc (x0 + round_size, y1 - round_size, round_size, 0.5 * Math.PI, Math.PI);
				else cr.line_to (x0, y1);
				if (up_left) cr.arc (x0 + round_size, y0 + round_size, round_size, Math.PI, 1.5 * Math.PI);
				else cr.line_to (x0, y0);

				cr.set_source (body_pattern);
				cr.fill ();

				if (widget.group_style != GroupStyle.LEFT) {
					if (down_left) cr.arc (x0 + round_size, y1 - round_size, round_size, 0.5 * Math.PI, Math.PI);
					else cr.move_to (x0, y1);
					if (up_left) cr.arc (x0 + round_size, y0 + round_size, round_size, Math.PI, 1.5 * Math.PI);
					else cr.line_to (x0, y0);

					cr.set_source_rgba (1, 1, 1, 0.8);
					cr.stroke ();
				}

				if (up_left) cr.arc (x0 + round_size, y0 + round_size, round_size, Math.PI, 1.5 * Math.PI);
				else cr.move_to (x0, y0);
				if (up_right) cr.arc (x1 - round_size, y0 + round_size, round_size, 1.5 * Math.PI, 0);
				else cr.line_to (x1, y0);
				if (down_right) cr.arc (x1 - round_size, y1 - round_size, round_size, 0, 0.5 * Math.PI);
				else cr.line_to (x1, y1);
				if (down_left) cr.arc (x0 + round_size, y1 - round_size, round_size, 0.5 * Math.PI, Math.PI);
				else cr.line_to (x0, y1);
				if (widget.group_style == GroupStyle.LEFT) {
					if (up_left) {
						cr.line_to (x0, y0 + round_size);
					} else {
						cr.line_to (x0, y0);
					}
				}

				cr.set_source_rgba (0, 0, 0, 0.2);
				cr.stroke ();
			}

			if (arrow_size > 0) {
				double x, y;

				switch (widget.image_position) {
				case Gtk.PositionType.BOTTOM:
				case Gtk.PositionType.TOP:
					x = body_allocation.x + (body_allocation.width - arrow_size) / 2.0;
					y = body_allocation.y + body_allocation.height - 2 * line_width
												- arrow_size - 2 * arrow_padding;

					if (draw_separator) {
						double left = body_allocation.x + 2 * line_width;
						double right = body_allocation.x + body_allocation.width - 2 * line_width;

						cr.move_to (left, y - line_width / 2);
						cr.line_to (right, y - line_width / 2);
						cr.set_source_rgba (0, 0, 0, 0.1);
						cr.stroke ();

						cr.move_to (left, y + line_width / 2);
						cr.line_to (right, y + line_width / 2);
						cr.set_source_rgba (1, 1, 1, 0.6);
						cr.stroke ();
					}

					y += arrow_padding;
					break;
				default:
					x = body_allocation.x + body_allocation.width - 2 * line_width
												- arrow_size - 2 * arrow_padding;
					y = body_allocation.y + (body_allocation.height - arrow_size) / 2.0;

					if (draw_separator) {
						double top = body_allocation.y + 2 * line_width;
						double bottom = body_allocation.y
										+ body_allocation.height
										- 2 * line_width;
						cr.move_to (x - line_width / 2, top);
						cr.line_to (x - line_width / 2, bottom);
						cr.set_source_rgba (0, 0, 0, 0.1);
						cr.stroke ();

						cr.move_to (x + line_width / 2, top);
						cr.line_to (x + line_width / 2, bottom);
						cr.set_source_rgba (1, 1, 1, 0.6);
						cr.stroke ();
					}

					x += arrow_padding;
					break;
				}

				y += arrow_size / 4.0 + line_width / 2.0;
				cr.move_to (x, y);
				cr.line_to (x + arrow_size, y);
				cr.line_to (x + arrow_size / 2.0, y + arrow_size / 2.0);
				cr.line_to (x, y);
				cr.set_source_rgba (0, 0, 0, 1.0);
				cr.fill ();
			}
		}

		/** Draws a gallery. */
		public void draw_gallery (Context cr, Gdk.Rectangle body_allocation,
								Gdk.Rectangle tiles_allocation, Gallery widget) {
			cr.set_source_rgba (0, 0, 0, 0.3);
			cr.set_line_width (1);
			cr.rectangle (
				tiles_allocation.x - 0.5,
				tiles_allocation.y - 0.5,
				tiles_allocation.width + 1.0,
				tiles_allocation.height + 1.0);
			cr.stroke ();
		}

		/** Draws a tile. */
		public void draw_tile (Context cr, Gdk.Rectangle body_allocation,
								Gdk.Rectangle content_allocation, Tile widget) {
			if (widget.selected) {
				var grad = new Pattern.linear (
					body_allocation.x,
					body_allocation.y,
					body_allocation.x,
					body_allocation.y + body_allocation.height);
				grad.add_color_stop_rgb (0.00, 0.9922, 0.7373, 0.4353);
				grad.add_color_stop_rgb (0.27, 0.9961, 0.8039, 0.5569);
				grad.add_color_stop_rgb (0.33, 0.9961, 0.7255, 0.4078);
				grad.add_color_stop_rgb (1.00, 0.9843, 0.8980, 0.6313);
				cr.set_source (grad);
				cr.rectangle (
					body_allocation.x,
					body_allocation.y,
					body_allocation.width,
					body_allocation.height);
				cr.fill ();
			}

			cr.set_source_rgb (1, 1, 1);
			cr.rectangle (
				content_allocation.x,
				content_allocation.y,
				content_allocation.width,
				content_allocation.height);
			cr.fill ();
		}
	}
}

